home *** CD-ROM | disk | FTP | other *** search
/ Die Speccy' 97 / Die Speccy' 97.iso / amiga_system / the_aminet / util / boot / snap_v2_0.lha / Snap / MinRexx.c < prev    next >
C/C++ Source or Header  |  1995-09-04  |  15KB  |  479 lines

  1. /*
  2.  *   This is an example of how REXX messages might be handled.  This is
  3.  *   a `minimum' example that both accepts asynchronous REXX messages and
  4.  *   can request REXX service.
  5.  *
  6.  *   Read this entire file!  It's short enough.
  7.  *
  8.  *   It is written in such a fashion that it can be attached to a program
  9.  *   with a minimum of fuss.  The only external symbols it makes available
  10.  *   are the seven functions and RexxSysBase.
  11.  *
  12.  *   This code is by Radical Eye Software, but it is put in the public
  13.  *   domain.  I would appreciate it if the following string was left in
  14.  *   both as a version check and as thanks from you for the use of this
  15.  *   code.
  16.  *
  17.  *   If you modify this file for your own use, don't bump the version
  18.  *   number; add a suffix, such as 1.0a or 1.0.3 or something, so we
  19.  *   don't have fake `versions' floating around.
  20.  */
  21.  
  22. #include "proto/MinRexx.h"
  23.  
  24. static char *blurb = "Radical Eye MinRexx 0.4";
  25.  
  26. /*
  27.  *   We read in our own personal little include.
  28.  */
  29. #include "minrexx.h"
  30. /*
  31.  *   All of our local globals, hidden from sight.
  32.  */
  33. static struct MsgPort *rexxPort;    /* this is *our* rexx port */
  34. static int bringerdown;        /* are we trying to shut down? */
  35. static struct rexxCommandList *globalrcl;    /* our command association list */
  36. static long stillNeedReplies;    /* how many replies are pending? */
  37. static long rexxPortBit;    /* what bit to wait on for Rexx? */
  38. static char *extension;        /* the extension for macros */
  39. static int (*userdisp) ();    /* the user's dispatch function */
  40. static struct RexxMsg *oRexxMsg;    /* the outstanding Rexx message */
  41.  
  42. /*
  43.  *   Our library base.  Don't you dare close this!
  44.  */
  45. struct Library *RexxSysBase;
  46.  
  47. /*
  48.  *   This is the main entry point into this code.
  49.  */
  50. /*
  51.  *   The first argument is the name of your port to be registered;
  52.  *   this will be used, for instance, with the `address' command of ARexx.
  53.  */
  54. /*
  55.  *   The second argument is an association list of command-name/user-data
  56.  *   pairs.  It's an array of struct rexxCommandList, terminated by a
  57.  *   structure with a NULL in the name field. The commands are case
  58.  *   sensitive.  The user-data field can contain anything appropriate,
  59.  *   perhaps a function to call or some other data.
  60.  */
  61. /*
  62.  *   The third argument is the file extension for ARexx macros invoked
  63.  *   by this program.  If you supply this argument, any `primitive' not
  64.  *   in the association list rcl will be sent out to ARexx for
  65.  *   interpretation, thus allowing macro programs to work just like
  66.  *   primitives.  If you do not want this behavior, supply a `NULL'
  67.  *   here, and those commands not understood will be replied with an
  68.  *   error value of RXERRORNOCMD.
  69.  */
  70. /*
  71.  *   The fourth argument is the user dispatch function.  This function
  72.  *   will *only* be called from rexxDisp(), either from the user calling
  73.  *   this function directly, or from dnRexxPort().  Anytime a command
  74.  *   match is found in the association list, this user-supplied function
  75.  *   will be called with two arguments---the Rexx message that was
  76.  *   received, and a pointer to the association pair.  This function
  77.  *   should return a `1' if the message was replied to by the function
  78.  *   and a `0' if the default success code of (0, 0) should be returned.
  79.  *   Note that the user function should never ReplyMsg() the message;
  80.  *   instead he should indicate the return values with replyRexxCmd();
  81.  *   otherwise we lose track of the messages that still lack replies.
  82.  */
  83. /*
  84.  *   upRexxPort() returns the signal bit to wait on for Rexx messages.
  85.  *   If something goes wrong, it simply returns a `0'.  Note that this
  86.  *   function is safe to call multiple times because we check to make
  87.  *   sure we haven't opened already.  It's also a quick way to change
  88.  *   the association list or dispatch function.
  89.  */
  90. long upRexxPort(     char *s, struct rexxCommandList *rcl, char *exten, int (*uf) ())
  91. {
  92.     struct MsgPort *FindPort();
  93.     struct MsgPort *CreatePort();
  94.  
  95. /*
  96.  *   Some basic error checking.
  97.  */
  98.     if (rcl == NULL || uf == NULL)
  99.         return (0L);
  100. /*
  101.  *   If we aren't open, we make sure no one else has opened a port with
  102.  *   this name already.  If that works, and the createport succeeds, we
  103.  *   fill rexxPortBit with the value to return.
  104.  *
  105.  *   Note that rexxPortBit will be 0 iff rexxPort is NULL, so the check
  106.  *   for rexxPort == NULL also insures that our rexxPortBit is 0.
  107.  */
  108.     if (rexxPort == NULL)
  109.     {
  110.         Forbid();
  111.         if (FindPort(s) == NULL)
  112.             rexxPort = CreatePort(s, 0L);
  113.         Permit();
  114.         if (rexxPort != NULL)
  115.             rexxPortBit = 1L << rexxPort->mp_SigBit;
  116.     }
  117. /*
  118.  *   Squirrel away these values for our own internal access, and return
  119.  *   the wait bit.
  120.  */
  121.     globalrcl = rcl;
  122.     extension = exten;
  123.     userdisp = uf;
  124.     return (rexxPortBit);
  125. }
  126. /*
  127.  *   This function closes the rexx library, but only if it is open
  128.  *   and we aren't expecting further replies from REXX.  It's
  129.  *   *private*, but it doesn't have to be; it's pretty safe to
  130.  *   call anytime.
  131.  */
  132. static void closeRexxLib( void)
  133. {
  134.     if (stillNeedReplies == 0 && RexxSysBase)
  135.     {
  136.         CloseLibrary((struct Library *)RexxSysBase);
  137.         RexxSysBase = NULL;
  138.     }
  139. }
  140. /*
  141.  *   This function closes down the Rexx port.  It is always safe to
  142.  *   call, and should *definitely* be made a part of your cleanup
  143.  *   routine.  No arguments and no return.  It removes the Rexx port,
  144.  *   replies to all of the messages and insures that we get replies
  145.  *   to all the ones we sent out, closes the Rexx library, deletes the
  146.  *   port, clears a few flags, and leaves.
  147.  */
  148. void dnRexxPort( void)
  149. {
  150.     if (rexxPort)
  151.     {
  152.         RemPort(rexxPort);
  153.         bringerdown = 1;
  154. /*
  155.  *   A message still hanging around?  We kill it off.
  156.  */
  157.         if (oRexxMsg)
  158.         {
  159.             oRexxMsg->rm_Result1 = RXERRORIMGONE;
  160.             ReplyMsg((struct Message *)oRexxMsg);
  161.             oRexxMsg = NULL;
  162.         }
  163.         while (stillNeedReplies)
  164.         {
  165.             WaitPort(rexxPort);
  166.             dispRexxPort();
  167.         }
  168.         closeRexxLib();
  169.         DeletePort(rexxPort);
  170.         rexxPort = NULL;
  171.     }
  172.     rexxPortBit = 0;
  173. }
  174.  
  175. /*
  176.  *   This is the function we use to see if the command matches
  177.  *   the command string.  Not case sensitive, and the real command only
  178.  *   need be a prefix of the command string.  Make sure all commands
  179.  *   are given in lower case!
  180.  */
  181. static int cmdcmp( register char *c, register char *m)
  182. {
  183.     while (*c && ((*c == *m) || (*c == *m + 32 && ('a' <= *c && *c <= 'z'))))
  184.     {
  185.         c++;
  186.         m++;
  187.     }
  188.     return ((int)*c);
  189. }
  190.  
  191. /*
  192.  *   Here we dispatch any REXX messages that might be outstanding.
  193.  *   This is the main routine for handling Rexx messages.
  194.  *   This function is fast if no messages are outstanding, so it's
  195.  *   pretty safe to call fairly often.
  196.  *
  197.  *   If we are bring the system down and flushing messages, we reply
  198.  *   with a pretty serious return code RXERRORIMGONE.
  199.  *
  200.  *   No arguments, no returns.
  201.  */
  202. void dispRexxPort( void)
  203. {
  204.     register struct RexxMsg *RexxMsg;
  205.     register struct rexxCommandList *rcl;
  206.     register char *p;
  207.     register int dontreply;
  208.  
  209. /*
  210.  *   If there's no rexx port, we're out of here.
  211.  */
  212.     if (rexxPort == NULL)
  213.         return;
  214. /*
  215.  *   Otherwise we have our normal loop on messages.
  216.  */
  217.     while (RexxMsg = (struct RexxMsg *)GetMsg(rexxPort))
  218.     {
  219. /*
  220.  *   If we have a reply to a message we sent, we look at the second
  221.  *   argument.  If it's set, it's a function we are supposed to call
  222.  *   so we call it.  Then, we kill the argstring and the message
  223.  *   itself, decrement the outstanding count, and attempt to close
  224.  *   down the Rexx library.  Note that this call only succeeds if
  225.  *   there are no outstanding messages.  Also, it's pretty quick, so
  226.  *   don't talk to me about efficiency.
  227.  */
  228.         if (RexxMsg->rm_Node.mn_Node.ln_Type == NT_REPLYMSG)
  229.         {
  230.             if (RexxMsg->rm_Args[1])
  231.             {
  232.                 ((int (*)())(RexxMsg->rm_Args[1])) (RexxMsg);
  233.             }
  234.             DeleteArgstring(RexxMsg->rm_Args[0]);
  235.             DeleteRexxMsg(RexxMsg);
  236.             stillNeedReplies--;
  237.             closeRexxLib();
  238. /*
  239.  *   The default case is we got a message and we need to check it for
  240.  *   primitives.  We skip past any initial tabs or spaces and initialize
  241.  *   the return code fields.
  242.  */
  243.         }
  244.         else
  245.         {
  246.             p = (char *)RexxMsg->rm_Args[0];
  247.             while (*p > 0 && *p <= ' ')
  248.                 p++;
  249.             RexxMsg->rm_Result1 = 0;
  250.             RexxMsg->rm_Result2 = 0;
  251. /*
  252.  *   If somehow the reply is already done or postponed, `dontreply' is
  253.  *   set.
  254.  */
  255.             dontreply = 0;
  256. /*
  257.  *   If the sky is falling, we just blow up and replymsg.
  258.  */
  259.             if (bringerdown)
  260.             {
  261.                 RexxMsg->rm_Result1 = RXERRORIMGONE;
  262. /*
  263.  *   Otherwise we cdr down our association list, comparing commands,
  264.  *   until we get a match.  If we get a match, we call the dispatch
  265.  *   function with the appropriate arguments, and break out.
  266.  */
  267.             }
  268.             else
  269.             {
  270.                 oRexxMsg = RexxMsg;
  271.                 for (rcl = globalrcl; rcl->name; rcl++)
  272.                 {
  273.                     if (cmdcmp(rcl->name, p) == 0)
  274.                     {
  275.                         userdisp(RexxMsg, rcl, p + strlen(rcl->name));
  276.                         break;
  277.                     }
  278.                 }
  279. /*
  280.  *   If we broke out, rcl will point to the command we executed; if we
  281.  *   are at the end of the list, we didn't understand the command.  In
  282.  *   this case, if we were supplied an extension in upRexxPort, we know
  283.  *   that we should send the command out, so we do so, synchronously.
  284.  *   The synchronous send takes care of our reply.  If we were given a
  285.  *   NULL extension, we bitch that the command didn't make sense to us.
  286.  */
  287.                 if (rcl->name == NULL)
  288.                 {
  289.                     if (extension)
  290.                     {
  291.                         syncRexxCmd(RexxMsg->rm_Args[0], RexxMsg);
  292.                         dontreply = 1;
  293.                     }
  294.                     else
  295.                     {
  296.                         RexxMsg->rm_Result1 = RXERRORNOCMD;
  297.                     }
  298.                 }
  299.             }
  300. /*
  301.  *   Finally, reply if appropriate.
  302.  */
  303.             oRexxMsg = NULL;
  304.             if (!dontreply)
  305.                 ReplyMsg((struct Message *)RexxMsg);
  306.         }
  307.     }
  308. }
  309.  
  310. /*
  311.  *   Opens the Rexx library if unopened.  Returns success (1) or
  312.  *   failure (0).  This is another function that is *private* but
  313.  *   that doesn't have to be.
  314.  */
  315. static int openRexxLib( void)
  316. {
  317.     if (RexxSysBase)
  318.         return TRUE;
  319.     return ((RexxSysBase = OpenLibrary(RXSNAME, 0L)) != NULL);
  320. }
  321. /*
  322.  *   This is the general ARexx command interface, but is not the one
  323.  *   you will use most of the time; ones defined later are easier to
  324.  *   understand and use.  But they all go through here.
  325.  */
  326. struct RexxMsg *sendRexxCmd(s, f, p1, p2, p3)
  327.     char *s;
  328.  
  329. /*
  330.  *   The first parameter is the command to send to Rexx.
  331.  */
  332.     int (*f) ();
  333.  
  334. /*
  335.  *   The second parameter is either NULL, indicating that the command
  336.  *   should execute asynchronously, or a function to be called when the
  337.  *   message we build up and send out here finally returns.  Please note
  338.  *   that the function supplied here could be called during cleanup after
  339.  *   a fatal error, so make sure it is `safe'.  This function always is
  340.  *   passed one argument, the RexxMsg that is being replied.
  341.  */
  342.     STRPTR p1, p2, p3;
  343.  
  344. /*
  345.  *   These are up to three arguments to be stuffed into the RexxMsg we
  346.  *   are building up, making the values available when the message is
  347.  *   finally replied to.  The values are stuffed into Args[2]..Args[4].
  348.  */
  349. {
  350.     struct RexxMsg *CreateRexxMsg();
  351.     STRPTR CreateArgstring();
  352.     register struct MsgPort *rexxport;
  353.     register struct RexxMsg *RexxMsg;
  354.  
  355. /*
  356.  *   If we have too many replies out there, we just return failure.
  357.  *   Note that you should check the return code to make sure your
  358.  *   message got out!  Then, we forbid, and make sure that:
  359.  *      - we have a rexx port open
  360.  *      - Rexx is out there
  361.  *      - the library is open
  362.  *      - we can create a message
  363.  *      - we can create an argstring
  364.  *
  365.  *   If all of these succeed, we stuff a few values and send the
  366.  *   message, permit, and return.
  367.  */
  368.     if (rexxPort == NULL || stillNeedReplies > MAXRXOUTSTANDING - 1)
  369.         return (NULL);
  370.     RexxMsg = NULL;
  371.     if (openRexxLib() && (RexxMsg =
  372.        CreateRexxMsg(rexxPort, extension, rexxPort->mp_Node.ln_Name)) &&
  373.         (RexxMsg->rm_Args[0] = CreateArgstring(s, (long)strlen(s))))
  374.     {
  375.         RexxMsg->rm_Action = RXCOMM;
  376.         RexxMsg->rm_Args[1] = (STRPTR) f;
  377.         RexxMsg->rm_Args[2] = p1;
  378.         RexxMsg->rm_Args[3] = p2;
  379.         RexxMsg->rm_Args[4] = p3;
  380.         RexxMsg->rm_Node.mn_Node.ln_Name = RXSDIR;
  381.         Forbid();
  382.         if (rexxport = FindPort(RXSDIR))
  383.             PutMsg(rexxport, (struct Message *)RexxMsg);
  384.         Permit();
  385.         if (rexxport)
  386.         {
  387.             stillNeedReplies++;
  388.             return (RexxMsg);
  389.         }
  390.         else
  391.             DeleteArgstring(RexxMsg->rm_Args[0]);
  392.     }
  393.     if (RexxMsg)
  394.         DeleteRexxMsg(RexxMsg);
  395.     closeRexxLib();
  396.     return (NULL);
  397. }
  398. /*
  399.  *   This function is used to send out an ARexx message and return
  400.  *   immediately.  Its single parameter is the command to send.
  401.  */
  402. struct RexxMsg *asyncRexxCmd( char *s)
  403. {
  404.     return (sendRexxCmd(s, NULL, NULL, NULL, NULL));
  405. }
  406. /*
  407.  *   This function sets things up to reply to the message that caused
  408.  *   it when we get a reply to the message we are sending out here.
  409.  *   But first the function we pass in, which actually handles the reply.
  410.  *   Note how we get the message from the Args[2]; Args[0] is the command,
  411.  *   Args[1] is this function, and Args[2]..Args[4] are any parameters
  412.  *   passed to sendRexxCmd() as p1..p3.  We pass the result codes right
  413.  *   along.
  414.  */
  415. static void replytoit( register struct RexxMsg *msg)
  416. {
  417.     register struct RexxMsg *omsg;
  418.  
  419.     omsg = (struct RexxMsg *)(msg->rm_Args[2]);
  420.     replyRexxCmd(omsg, msg->rm_Result1, msg->rm_Result2, NULL);
  421.     ReplyMsg((struct Message *)omsg);
  422. }
  423. /*
  424.  *   This function makes use of everything we've put together so far,
  425.  *   and functions as a synchronous Rexx call; as soon as the macro
  426.  *   invoked here returns, we reply to `msg', passing the return codes
  427.  *   back.
  428.  */
  429. struct RexxMsg *syncRexxCmd( char *s, struct RexxMsg *msg)
  430. {
  431.     return (sendRexxCmd(s, (APTR) & replytoit, msg, NULL, NULL));
  432. }
  433. /*
  434.  *   There are times when you want to pass back return codes or a
  435.  *   return string; call this function when you want to do that,
  436.  *   and return `1' from the user dispatch function so the main
  437.  *   event loop doesn't reply (because we reply here.)  This function
  438.  *   always returns 1.
  439.  */
  440. void replyRexxCmd(
  441. /*
  442.  *   The first parameter is the message we are replying to.
  443.  */
  444.     register struct RexxMsg *msg,
  445.  
  446. /*
  447.  *   The next two parameters are the primary and secondary return
  448.  *   codes.
  449.  */
  450.     register long primary,
  451.     register long secondary,
  452.  
  453. /*
  454.  *   The final parameter is a return string.  This string is only
  455.  *   returned if the primary return code is 0, and a string was
  456.  *   requested.
  457.  *
  458.  *   We also note that we have replied to the message that came in.
  459.  */
  460.     register char *string)
  461. {
  462.     STRPTR CreateArgstring();
  463.  
  464. /*
  465.  *   Note how we make sure the Rexx Library is open before calling
  466.  *   CreateArgstring . . . and we close it down at the end, if possible.
  467.  */
  468.     if (primary == 0 && (msg->rm_Action & (1L << RXFB_RESULT)))
  469.     {
  470.         if (string && openRexxLib())
  471.             secondary = (long)CreateArgstring(string, (long)strlen(string));
  472.         else
  473.             secondary = 0L;
  474.     }
  475.     msg->rm_Result1 = primary;
  476.     msg->rm_Result2 = secondary;
  477.     closeRexxLib();
  478. }
  479.